`include "PRV564Config.v"
`include "PRV564Define.v"

// Author: Dingbang Liu
// Description: MD Pipelien decoder

module md_decode(
// MD Pipeline global signals
    input wire clk,
    input wire arst,
    input wire stall,
    input wire flush,
// Input
    input wire handshake,
    input wire[7:0] i_opcode,
    input wire[1:0] i_opinfo,
    input wire[63:0] i_data1,
    input wire[63:0] i_data2,
    input wire[7:0] i_tag,
    input wire[1:0] i_priv,
    input wire[`XLEN-1:0] i_pc,
// Output
    output reg[127:0] ex_data1,
    output reg[127:0] ex_data2,
    output reg ex_div, // 0 for mul, 1 for div
    output reg[7:0] ex_opcode,
    output reg[1:0] ex_opinfo,
    output reg[7:0] ex_tag,
    output reg[1:0] ex_priv,
    output reg[`XLEN-1:0] ex_pc
    );
    
    reg[127:0] ex_data1_next;
    reg[127:0] ex_data2_next;
    reg ex_div_next;
    reg[7:0] ex_opcode_next;
    reg[1:0] ex_opinfo_next;
    reg[7:0] ex_tag_next;
    reg[1:0] ex_priv_next;
    reg[`XLEN-1:0] ex_pc_next;
    wire[63:0] rs1;
    wire[63:0] rs2;
    wire div;
    wire sign1, sign2;
    wire[127:0] mul_data1;
    wire[127:0] mul_data2;

    assign rs1 = i_opinfo == `Sign32 ? {{32{i_data1[31]}}, i_data1[31:0]} : i_opinfo == `Unsign32 ? {32'b0, i_data1[31:0]} : i_data1;
    assign rs2 = i_opinfo == `Sign32 ? {{32{i_data2[31]}}, i_data2[31:0]} : i_opinfo == `Unsign32 ? {32'b0, i_data2[31:0]} : i_data2;
    assign div = i_opcode == `Mcop_DIV || i_opcode == `Mcop_REM;
    assign sign1 = i_opinfo == `Sign64 || i_opinfo == `Sign32 || i_opcode == `Mcop_MULHS;
    assign sign2 = i_opinfo == `Sign64 || i_opinfo == `Sign32;
    mul u_mul(.rs1(rs1), .rs2(rs2), .sign1(sign1), .sign2(sign2), .x(mul_data1), .y(mul_data2));
    
    always @ (*) begin
        if (flush) begin
            ex_data1_next = 128'b0;
            ex_data2_next = 128'b0;
            ex_div_next = 1'b0;
            ex_opcode_next = 8'b0;
            ex_opinfo_next = 2'b0;
            ex_tag_next = 8'b0;
            ex_priv_next = 2'b0;
            ex_pc_next = `XLEN'b0;
        end
        else if (stall) begin
            ex_data1_next = ex_data1;
            ex_data2_next = ex_data2;
            ex_div_next = ex_div;
            ex_opcode_next = ex_opcode;
            ex_opinfo_next = ex_opinfo;
            ex_tag_next = ex_tag;
            ex_priv_next = ex_priv;
            ex_pc_next = ex_pc;
        end
        else if (handshake & div) begin
            ex_data1_next = {64'b0, i_data1};
            ex_data2_next = {64'b0, i_data2};
            ex_div_next = 1'b1;
            ex_opcode_next = i_opcode;
            ex_opinfo_next = i_opinfo;
            ex_tag_next = i_tag;
            ex_priv_next = i_priv;
            ex_pc_next = i_pc;
        end
        else if (handshake) begin
            ex_data1_next = mul_data1;
            ex_data2_next = mul_data2;
            ex_div_next = 1'b0;
            ex_opcode_next = i_opcode;
            ex_opinfo_next = i_opinfo;
            ex_tag_next = i_tag;
            ex_priv_next = i_priv;
            ex_pc_next = i_pc;
        end
        else begin
            ex_data1_next = 128'b0;
            ex_data2_next = 128'b0;
            ex_div_next = 1'b0;
            ex_opcode_next = 8'b0;
            ex_opinfo_next = 2'b0;
            ex_tag_next = 8'b0;
            ex_priv_next = 2'b0;
            ex_pc_next = `XLEN'b0;
        end
    end

    always @ (posedge clk or posedge arst) begin
        if (arst) begin
            ex_data1 <= 128'b0;
            ex_data2 <= 128'b0;
            ex_div <= 1'b0;
            ex_opcode <= 8'b0;
            ex_opinfo <= 2'b0;
            ex_tag <= 8'b0;
            ex_priv <= 2'b0;
            ex_pc <= `XLEN'b0;
        end
        else begin
            ex_data1 <= ex_data1_next;
            ex_data2 <= ex_data2_next;
            ex_div <= ex_div_next;
            ex_opcode <= ex_opcode_next;
            ex_opinfo <= ex_opinfo_next;
            ex_tag <= ex_tag_next;
            ex_priv <= ex_priv_next;
            ex_pc <= ex_pc_next;
        end
    end
    
endmodule

